﻿using System;
using System.Linq;
using CodeAngel.Domain.Framework;
//using CodeAngel.Domain.Services;

namespace CodeAngel.Domain.Aggregates
{
    /// <summary>
    /// Since this aggregate includes recursive objects in its graph (e.g., DataElements of an Aggregate), we need 
    /// to handle building this graph manually.
    /// </summary>
	public partial class DomainFullAggregate
    {

        #region CustomBuildObjectGraph
        
        /// <summary>
        /// Since this aggregate includes recursive objects in its graph (e.g., DataElements of an Aggregate), we need 
        /// to handle building this graph manually.
        /// </summary>
        partial void CustomBuildObjectGraph()
        {
            this._BuildObjectGraphIsHandled = true;

            this._RootDomain = new Domain(this);
            var ThisDomainDTO = this.DtoHost.RootDomains[0];
            this._RootDomain.InitializeAllScalarProperties(ThisDomainDTO);

            this.BuildProductionDatabase(this._RootDomain, ThisDomainDTO);
            this.BuildDevelopmentDatabase(this._RootDomain, ThisDomainDTO);
            this.BuildEntities(this._RootDomain, ThisDomainDTO);

            base.Root = this._RootDomain;
        }

        private Domain _RootDomain;

        #region BuildProductionDatabase

        private void BuildProductionDatabase(Domain domain, DomainDto domainDto)
        {
            var ThisProductionDatabaseDto = this.DtoHost.Databases.Find<DatabaseDto>(domainDto.ProductionDatabaseID);
            if (ThisProductionDatabaseDto != null)
            {
                var ThisProductionDatabase = new Database(this, domain);
                domain.InitializeProductionDatabaseProperty(ThisProductionDatabase);
                ThisProductionDatabase.InitializeAllScalarProperties(ThisProductionDatabaseDto);
            }
            else
            {
                domain.InitializeProductionDatabaseProperty();
            }
        }

        #endregion

        #region BuildDevelopmentDatabase

        private void BuildDevelopmentDatabase(Domain domain, DomainDto domainDto)
        {
            var ThisDevelopmentDatabaseDto = this.DtoHost.Databases.Find<DatabaseDto>(domainDto.DevelopmentDatabaseID);
            if (ThisDevelopmentDatabaseDto != null)
            {
                var ThisDevelopmentDatabase = new Database(this, domain);
                domain.InitializeDevelopmentDatabaseProperty(ThisDevelopmentDatabase);
                ThisDevelopmentDatabase.InitializeAllScalarProperties(ThisDevelopmentDatabaseDto);
            }
            else
            {
                domain.InitializeDevelopmentDatabaseProperty();
            }
        }

        #endregion

        #region BuildEntities

        private void BuildEntities(Domain domain, DomainDto domainDto)
        {
            domain.InitializeEntityDefsProperty();
            foreach (var ThisEntityDefDto in this.DtoHost.EntityDefs.FindAll(ThisDto => ThisDto.DomainID.Equals(domainDto.ID)))
            {
                var ThisED = new EntityDef(this);
                domain.EntityDefs.Add(ThisED);
                ThisED.InitializeDomainProperty(domain);
                ThisED.InitializeAllScalarProperties(ThisEntityDefDto);
                this.BuildEntityPropertyDefs(ThisED, ThisEntityDefDto);
            }

            // Build up ForeignKeyPropertyDef, which must be done after all EntityDefs and 
            // their PropertyDefs have been created.
            foreach (var ThisPdDto in this.DtoHost.PropertyDefs)
            {
                var TargetPD = this._RootDomain.FindPropertyDef(ThisPdDto.ID);
                if (ThisPdDto.ForeignKeyPropertyDefID.HasValue)
                {
                    var ForeignKeyPD = this._RootDomain.FindPropertyDef(ThisPdDto.ForeignKeyPropertyDefID.Value);
                    TargetPD.InitializeForeignKeyPropertyDefProperty(ForeignKeyPD);
                    this._RootDomain.FindPropertyDef(ThisPdDto.ID).ForeignKeyPropertyDef = this._RootDomain.FindPropertyDef(ThisPdDto.ForeignKeyPropertyDefID.Value);
                }
                else
                {
                    TargetPD.InitializeForeignKeyPropertyDefProperty();
                }
            }


            // Building the FilterRelations and AggregateDefs must be done after building the 
            // EntityDefs and the ForeignKeyPropertyDef, since these reference the PropertyDefs 
            // of those EntityDefs.
            foreach (var ThisED in domain.EntityDefs)
            {
                var ThisEntityDefDto = this.DtoHost.EntityDefs.Find(ThisED.ID);
                this.BuildEntityFilterRelations(ThisED, ThisEntityDefDto);
                this.BuildEntityAggregates(ThisED, ThisEntityDefDto);
            }
        }

        #region BuildEntityFilterRelations

        private void BuildEntityFilterRelations(EntityDef EntityDef, EntityDefDto EntityDefDto)
        {
            var FilterRelationDto = this.DtoHost.FilterRelations.Find<FilterRelationDto>(EntityDefDto.FilterRelationID);
            if (FilterRelationDto != null)
            {
                var ThisFilterRelation = new FilterRelation(this, EntityDef);
                this.ConstructFilterRelation(ThisFilterRelation, null, FilterRelationDto, EntityDef);
                EntityDef.InitializeFilterRelationProperty(ThisFilterRelation);
            }
            else
            {
                EntityDef.InitializeFilterRelationProperty();
            }
        }

        private void ConstructFilterRelation(FilterRelation filterRelation, FilterRelation parentFilterRelation,
                                             FilterRelationDto filterRelationDto, EntityDef parentEntityDef)
        {
            filterRelation.InitializeEntityDefProperty(parentEntityDef);
            filterRelation.InitializeParentFilterRelationProperty(parentFilterRelation);
            filterRelation.InitializeAllScalarProperties(filterRelationDto);

            // Map back to our property definition, if there is one.
            PropertyDef ThisPD = null;
            if (filterRelationDto.PropertyDefID.HasValue)
                ThisPD = this._RootDomain.FindPropertyDef(filterRelationDto.PropertyDefID.Value);
            filterRelation.InitializePropertyDefProperty(ThisPD);


            // Create any child FilterRelations
            filterRelation.InitializeFilterRelationsProperty();
            foreach (var ChildFilterRelationDto in this.DtoHost.FilterRelations.FindAll(ThisDto => ThisDto.ParentFilterRelationID.Equals(filterRelationDto.ID)))
            {
                var ChildFilterRelation = new FilterRelation(this, filterRelation);
                this.ConstructFilterRelation(ChildFilterRelation, filterRelation, ChildFilterRelationDto, parentEntityDef);
                filterRelation.FilterRelations.Add(ChildFilterRelation);
            }

        }

        #endregion

        #region BuildEntityPropertyDefs

        private void BuildEntityPropertyDefs(EntityDef EntityDef, EntityDefDto EntityDefDto)
        {
            EntityDef.InitializePropertyDefsProperty();
            foreach (var ThisDTO in this.DtoHost.PropertyDefs.FindAll(ThisDto => ThisDto.EntityDefID.Equals(EntityDefDto.ID)))
            {
                var ThisPD = new PropertyDef(this, EntityDef);
                EntityDef.PropertyDefs.Add(ThisPD);
                
                ThisPD.InitializeEntityDefProperty(EntityDef);
                ThisPD.InitializeAllScalarProperties(ThisDTO);

                //var ForeignKeyPropertyDefDto = this.DtoHost.PropertyDefs.Find<PropertyDefDto>(ThisDTO.ForeignKeyPropertyDefID);
                //if (ForeignKeyPropertyDefDto != null)
                //{
                //    var ForeignKeyPropertyDef = new PropertyDef(this, ThisPD);
                //    ThisPD.InitializeForeignKeyPropertyDefProperty(ForeignKeyPropertyDef);
                //    ForeignKeyPropertyDef.InitializeAllScalarProperties(ForeignKeyPropertyDefDto);
                //    ForeignKeyPropertyDef.InitializeForeignKeyPropertyDefProperty(ThisPD);
                //    //ForeignKeyPropertyDef.InitializePropertyDefIDProperty(ForeignKeyPropertyDefDto.PropertyDefID);
                //    //ForeignKeyPropertyDef.InitializeDatabaseColumnNameProperty(ForeignKeyPropertyDefDto.DatabaseColumnName);
                //}
                //else
                //{
                //    ThisPD.InitializeForeignKeyPropertyDefProperty();
                //}
            }
        }

        #endregion

        #region BuildEntityAggregates

        private void BuildEntityAggregates(EntityDef EntityDef, EntityDefDto EntityDefDto)
        {
            EntityDef.InitializeAggregateDefsProperty();
            var AggregateDefDtos = this.DtoHost.AggregateDefs.FindAll(ThisDto => ThisDto.EntityDefID.Equals(EntityDefDto.ID));
            foreach (var ThisAggregateDefDto in AggregateDefDtos)
            {
                var ThisAggregateDef = new AggregateDef(this, EntityDef);
                ThisAggregateDef.InitializeAllScalarProperties(ThisAggregateDefDto);
                ThisAggregateDef.InitializeEntityDefProperty(EntityDef);
                EntityDef.AggregateDefs.Add(ThisAggregateDef);

                // DataElement
                var ThisDataElementDto = this.DtoHost.DataElements.Find(ThisAggregateDefDto.DataElementID);
                var ThisDataElement = new DataElement(this, ThisAggregateDef);
                this.ConstructDataElement(ThisDataElement, ThisDataElementDto, ThisAggregateDef);
                ThisDataElement.InitializeParentDataElementProperty();
                ThisAggregateDef.InitializeDataElementProperty(ThisDataElement);

            }
        }

        private void ConstructDataElement(DataElement dataElement, DataElementDto dataElementDto, AggregateDef AggregateDef)
        {
            dataElement.InitializeAllScalarProperties(dataElementDto);
            dataElement.InitializeAggregateDefProperty(AggregateDef);

            // Map back to our Property Definition, if we have one.
            PropertyDef ThisPD = null;
            if (dataElementDto.PropertyDefID.HasValue)
                ThisPD = this._RootDomain.FindPropertyDef(dataElementDto.PropertyDefID.Value);
            dataElement.InitializePropertyDefProperty(ThisPD);

            // Create any child data elements.
            dataElement.InitializeDataElementsProperty();
            var ChildDataElementDtos = this.DtoHost.DataElements.FindAll<DataElementDto>(ThisDto => ThisDto.ParentDataElementID.Equals(dataElementDto.ID));
            foreach (var ChildDataElementDto in ChildDataElementDtos)
            {
                var ChildDE = new DataElement(this, dataElement);
                this.ConstructDataElement(ChildDE, ChildDataElementDto, AggregateDef);
                ChildDE.InitializeParentDataElementProperty(dataElement);
                dataElement.DataElements.Add(ChildDE);
            }

        }

        #endregion

        #endregion

        #endregion

        #region CustomSynchronizeDtos

        /// <summary>
        /// Since this aggregate includes recursive objects in its graph (e.g., DataElements of 
        /// an Aggregate, FilterRelations of a EntityDef), we need to handle syncing this 
        /// graph manually.
        /// </summary>
        partial void CustomSynchronizeDtos()
        {
            this._SynchronizeDtosIsHandled = true;

            this.Root.SynchronizeDto(this.DtoHost.RootDomains);

            // Development Database
            var developmentDatabase = this.Root.DevelopmentDatabase;
            if (developmentDatabase != null)
            {
                developmentDatabase.SynchronizeDto(this.DtoHost.Databases);
            }

            // Production Database
            var productionDatabase = this.Root.ProductionDatabase;
            if (productionDatabase != null)
            {
                productionDatabase.SynchronizeDto(this.DtoHost.Databases);
            }

            // EntityDefs
            foreach (var ThisED in this.Root.EntityDefs)
            {
                ThisED.SynchronizeDto(this.DtoHost.EntityDefs);

                // FilterRelation
                if (ThisED.FilterRelation != null)
                {
                    this.SynchronizeFilterRelation(ThisED.FilterRelation);
                }

                // AggregateDefs
                foreach (var ThisAD in ThisED.AggregateDefs)
                {
                    ThisAD.SynchronizeDto(this.DtoHost.AggregateDefs);
                    this.SynchronizeDataElement(ThisAD.DataElement);
                }

                // PropertyDefs
                foreach (var PropertyDef19 in ThisED.PropertyDefs)
                {
                    PropertyDef19.SynchronizeDto(this.DtoHost.PropertyDefs);
                    var PropertyDef20 = PropertyDef19.ForeignKeyPropertyDef;
                    if (PropertyDef20 != null)
                    {
                        PropertyDef20.SynchronizeDto(this.DtoHost.PropertyDefs);
                    }
                }
            }
        }

        private void SynchronizeDataElement(DataElement thisDE)
        {
            thisDE.SynchronizeDto(this.DtoHost.DataElements);
            foreach (var ChildDE in thisDE.DataElements)
                SynchronizeDataElement(ChildDE);
        }


        private void SynchronizeFilterRelation(FilterRelation thisFR)
        {
            thisFR.SynchronizeDto(this.DtoHost.FilterRelations);
            foreach (var ChildFR in thisFR.FilterRelations)
                this.SynchronizeFilterRelation(ChildFR);
        }

        #endregion

    }
}
